import { spawnSync } from "node:child_process";
import { readFileSync, statSync } from "node:fs";
import path from "node:path";

/**
 * @file Lit Localize build script.
 *
 * @remarks
 * Determines if all the Xliff translation source files are present and
 * if the Typescript source files generated from those sources are up-to-date.
 *
 * If they are not, it runs the locale building script, intercepting the
 * long spew of "this string is not translated" and replacing it with a
 * summary of how many strings are missing with respect to the source locale.
 *
 * @import { ConfigFile } from "@lit/localize-tools/lib/types/config.js"
 * @import { Stats } from "node:fs";
 */
import { PackageRoot } from "#paths/node";

/**
 * @type {ConfigFile}
 */
const localizeRules = JSON.parse(
    readFileSync(path.join(PackageRoot, "lit-localize.json"), "utf-8"),
);

/**
 *
 * @param {string} loc
 * @returns {boolean}
 */
function generatedFileIsUpToDateWithXliffSource(loc) {
    const xliff = path.join("./xliff", `${loc}.xlf`);
    const gened = path.join("./src/locales", `${loc}.ts`);

    // Returns false if: the expected XLF file doesn't exist, The expected
    // generated file doesn't exist, or the XLF file is newer (has a higher date)
    // than the generated file.  The missing XLF file is important enough it
    // generates a unique error message and halts the build.

    /**
     * @type {Stats}
     */
    let xlfStat;

    try {
        xlfStat = statSync(xliff);
    } catch (_error) {
        console.error(`lit-localize expected '${loc}.xlf', but XLF file is not present`);
        process.exit(1);
    }

    /**
     * @type {Stats}
     */
    let genedStat;

    // If the generated file doesn't exist, of course it's not up to date.
    try {
        genedStat = statSync(gened);
    } catch (_error) {
        return false;
    }

    // if the generated file is the same age or newer (date is greater) than the xliff file, it's
    // presumed to have been generated by that file and is up-to-date.
    return genedStat.mtimeMs >= xlfStat.mtimeMs;
}

// For all the expected files, find out if any aren't up-to-date.
const upToDate = localizeRules.targetLocales.reduce(
    (acc, loc) => acc && generatedFileIsUpToDateWithXliffSource(loc),
    true,
);

if (!upToDate) {
    const status = spawnSync("npm", ["run", "build-locales:build"], { encoding: "utf8" });

    // Count all the missing message warnings
    const counts = status.stderr.split("\n").reduce((acc, line) => {
        const match = /^([\w-]+) message/.exec(line);
        if (!match) {
            return acc;
        }
        acc.set(match[1], (acc.get(match[1]) || 0) + 1);
        return acc;
    }, new Map());

    const locales = Array.from(counts.keys());
    locales.sort();

    const report = locales
        .map((locale) => `Locale '${locale}' has ${counts.get(locale)} missing translations`)
        .join("\n");

    console.log(`Translation tables rebuilt.\n${report}\n`);
}

console.log("Locale ./src is up-to-date");
